#ifndef POPUP_MESSAGES_ID
#define POPUP_MESSAGES_ID

#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif

#include <PopupOSAL.hpp>
#include <PopupSSL.hpp>
#include <PopupUtilities.hpp>
#include <map>

namespace Popup
{

  #define POPUP_MAGIC 0xBEBE

  //============================================================================
  // All popup messages
  //============================================================================

  typedef enum
  {
    POPUP_MSG_TYPE_UNK,
    POPUP_MSG_TYPE_RSA_PUBLIC_KEY,
    POPUP_MSG_TYPE_BLOWFISH_KEY,
    POPUP_MSG_TYPE_LOGIN,
    POPUP_MSG_TYPE_CONNECTION_ACK,
    POPUP_MSG_TYPE_USER_UPDATE,
    POPUP_MSG_TYPE_MESSAGE,
    POPUP_MSG_TYPE_RATE_MESSAGE,
    POPUP_MSG_TYPE_KILL_ORDER,
    POPUP_MSG_TYPE_PING,
    POPUP_MSG_TYPE_PONG,
    POPUP_MSG_TYPE_STOP_ORDER,
    POPUP_MSG_TYPE_ATTACHMENT,
    POPUP_MSG_TYPE_CANVASS_SUBMIT,
    POPUP_MSG_TYPE_CANVASS_VOTE,
    POPUP_MSG_TYPE_STATISTICS_UPDATE,
    POPUP_MSG_TYPE_INVITATION,
    POPUP_MSG_TYPE_KEEPALIVE_PROBE,
    POPUP_MSG_TYPE_KEEPALIVE_ACK
  } MsgType;

  typedef enum
  {
    POPUP_FIELD_DATA_TYPE_UNK       = 0x0000,
    POPUP_FIELD_DATA_TYPE_SHORT     = 0x0001,
    POPUP_FIELD_DATA_TYPE_LONG      = 0x0002,
    POPUP_FIELD_DATA_TYPE_STRING    = 0x0003,
    POPUP_FIELD_DATA_TYPE_BOOLEAN   = 0x0004,
    POPUP_FIELD_DATA_TYPE_LONG_LONG = 0x0005
  } MsgFieldType;

  typedef enum
  {
    POPUP_ENCRYPTION_NONE       = 0x0000,
    POPUP_ENCRYPTION_RSA        = 0x0001,
    POPUP_ENCRYPTION_BLOWFISH   = 0x0002
  } MsgEncryption;

  //============================================================================
  // Message data structures
  //============================================================================

  struct RawMessageHeader
  {
    unsigned short  magic;
    unsigned int   size;
    unsigned short  encryption;
  };

  struct MessageHeader
  {
    unsigned short type;
    unsigned short spare[29];
    unsigned int   payloadSize;
  };

  struct Message
  {
    MessageHeader header;
    unsigned char payload[];
  };

  struct RawMessage
  {
    RawMessageHeader header;
    Message        message;
  };

  struct MessageField
  {
    unsigned short id;
    unsigned short type;
    unsigned int   size;
    unsigned char  data[];
  };

  typedef std::map<unsigned short, MessageField*> FieldsMap;

  //============================================================================
  // Message recv/send utility class
  //============================================================================

  struct AbstractMessage;

  class MessageNetworker {
  public:
    MessageNetworker(PopupUtils::Socket *p_socket,
                     const SslContext *p_sslContext)
    : socket(p_socket), sslContext(p_sslContext) {}

    ~MessageNetworker() {}

    bool receive(Message **p_message,
                 RawMessage **p_fullMessage = 0,
                 unsigned int  *p_fullMessageSize = 0);

    bool send(AbstractMessage *p_message,
              const SslContext *p_sslContext = 0);

    bool const_send(AbstractMessage *p_message,
                    const SslContext *p_sslContext = 0);

    bool send(const RawMessage *p_message, unsigned int  p_size);

  private:
    bool send(unsigned char *p_message,
              size_t p_size,
              MsgEncryption p_encryption,
              const SslContext *p_sslContext);

    bool send(const unsigned char *p_message,
              size_t p_size,
              MsgEncryption p_encryption,
              const SslContext *p_sslContext);

  private:
    PopupUtils::Socket *socket;
    const SslContext *sslContext;
  };

  //============================================================================
  // Base types init, endianness and dump
  //============================================================================

  void RawMessageHeader_initialize(RawMessageHeader *p_header);
  void RawMessageHeader_ntoh(RawMessageHeader *p_header);
  void RawMessageHeader_hton(RawMessageHeader *p_header);
  void RawMessageHeader_ntoh(RawMessageHeader & p_dest,
                             const RawMessageHeader & p_src);
  void RawMessageHeader_hton(RawMessageHeader & p_dest,
                             const RawMessageHeader & p_src);
  void MessageHeader_initialize(MessageHeader *p_header);
  void MessageHeader_ntoh(MessageHeader *p_header);
  void MessageHeader_hton(MessageHeader *p_header);
  void ntohll(unsigned long long *value);
  void htonll(unsigned long long *value);
  void MessageField_initialize(MessageField *p_field);
  void MessageField_ntoh(MessageField *p_field);
  void MessageField_hton(MessageField *p_field);
  void MessageField_dump(MessageField *p_field);
  void Message_initialize(Message *p_msg);
  void Message_ntoh(Message *p_msg);
  void Message_hton(Message *p_msg);
}

#endif
